use std::collections::HashMap;
use std::fmt;

use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::CommonConfigError;

/// 项目阶段枚举
///
/// 预置预算、结算、审核三类，同时保留 Custom 以支持项目自定义阶段。
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ProjectPhase {
    Budget,
    Settlement,
    Audit,
    Custom(String),
}

impl ProjectPhase {
    /// 转换为字符串表示
    pub fn as_str(&self) -> &str {
        match self {
            ProjectPhase::Budget => "budget",
            ProjectPhase::Settlement => "settlement",
            ProjectPhase::Audit => "audit",
            ProjectPhase::Custom(value) => value.as_str(),
        }
    }
}

impl fmt::Display for ProjectPhase {
    fn fmt(
        &self,
        f: &mut fmt::Formatter<'_>,
    ) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

impl Serialize for ProjectPhase {
    fn serialize<S>(
        &self,
        serializer: S,
    ) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.as_str())
    }
}

impl<'de> Deserialize<'de> for ProjectPhase {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value = String::deserialize(deserializer)?;
        match value.to_lowercase().as_str() {
            "budget" => Ok(ProjectPhase::Budget),
            "settlement" => Ok(ProjectPhase::Settlement),
            "audit" => Ok(ProjectPhase::Audit),
            _ => Ok(ProjectPhase::Custom(value)),
        }
    }
}

/// 区域画像
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct RegionProfile {
    pub code: String,
    pub name: String,
}

impl RegionProfile {
    pub fn new(
        code: impl Into<String>,
        name: impl Into<String>,
    ) -> Self {
        Self { code: code.into(), name: name.into() }
    }
}

/// 项目画像
///
/// 记录引导流程所需的项目关键信息。
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectProfile {
    pub project_id: String,
    pub phase: ProjectPhase,
    pub project_name: Option<String>,
    pub tenant_id: Option<String>,
    pub region: Option<RegionProfile>,
    pub version: Option<String>,
    pub metadata: HashMap<String, String>,
}

impl ProjectProfile {
    pub fn new(
        project_id: impl Into<String>,
        phase: ProjectPhase,
    ) -> Self {
        Self { project_id: project_id.into(), phase, project_name: None, tenant_id: None, region: None, version: None, metadata: HashMap::new() }
    }

    /// 构建器入口
    pub fn builder() -> ProjectProfileBuilder {
        ProjectProfileBuilder::default()
    }

    pub fn phase(&self) -> &ProjectPhase {
        &self.phase
    }

    pub fn region(&self) -> Option<&RegionProfile> {
        self.region.as_ref()
    }

    pub fn metadata(&self) -> &HashMap<String, String> {
        &self.metadata
    }
}

/// 项目画像构建器
#[derive(Debug, Default)]
pub struct ProjectProfileBuilder {
    project_id: Option<String>,
    phase: Option<ProjectPhase>,
    project_name: Option<String>,
    tenant_id: Option<String>,
    region: Option<RegionProfile>,
    version: Option<String>,
    metadata: HashMap<String, String>,
}

impl ProjectProfileBuilder {
    pub fn project_id(
        mut self,
        project_id: impl Into<String>,
    ) -> Self {
        self.project_id = Some(project_id.into());
        self
    }

    pub fn phase(
        mut self,
        phase: ProjectPhase,
    ) -> Self {
        self.phase = Some(phase);
        self
    }

    pub fn project_name(
        mut self,
        name: impl Into<String>,
    ) -> Self {
        self.project_name = Some(name.into());
        self
    }

    pub fn tenant_id(
        mut self,
        tenant_id: impl Into<String>,
    ) -> Self {
        self.tenant_id = Some(tenant_id.into());
        self
    }

    pub fn region(
        mut self,
        region: RegionProfile,
    ) -> Self {
        self.region = Some(region);
        self
    }

    pub fn version(
        mut self,
        version: impl Into<String>,
    ) -> Self {
        self.version = Some(version.into());
        self
    }

    pub fn metadata(
        mut self,
        key: impl Into<String>,
        value: impl Into<String>,
    ) -> Self {
        self.metadata.insert(key.into(), value.into());
        self
    }

    /// 构建并校验
    pub fn build(self) -> Result<ProjectProfile, CommonConfigError> {
        let project_id = self.project_id.ok_or_else(|| CommonConfigError::MissingRequired("project_id".to_string()))?;
        if project_id.trim().is_empty() {
            return Err(CommonConfigError::InvalidValue { key: "project_id".to_string(), value: project_id });
        }

        let phase = self.phase.ok_or_else(|| CommonConfigError::MissingRequired("phase".to_string()))?;

        Ok(ProjectProfile {
            project_id,
            phase,
            project_name: self.project_name,
            tenant_id: self.tenant_id,
            region: self.region,
            version: self.version,
            metadata: self.metadata,
        })
    }
}
